package clw.toy;


import java.util.List;

/**
 * @author chengliwei
 */
public abstract class Stmt {
    interface Visitor<R> {
        R visitExpressionStmt(Expression expression);

        R visitPrintStmt(Print print);

        R visitVarStmt(Var var);

        R visitBlockStmt(Block block);

        R visitClassStmt(Class clazz);

        R visitFunctionStmt(Function function);

        R visitIfStmt(If ifStmt);

        R visitReturnStmt(Return returnStmt);

        R visitWhileStmt(While whileStmt);
    }

    abstract <R> R accept(Visitor<R> visitor);

    static class Print extends Stmt {

        Expr value;

        public Print(Expr value) {
            this.value = value;
        }

        @Override
        <R> R accept(Visitor<R> visitor) {
            return visitor.visitPrintStmt(this);
        }
    }

    static class Expression extends Stmt {
        Expr expr;

        public Expression(Expr expr) {
            this.expr = expr;
        }

        @Override
        <R> R accept(Visitor<R> visitor) {
            return visitor.visitExpressionStmt(this);
        }
    }

    static class Var extends Stmt {

        Token name;
        Expr initializer;

        public Var(Token name, Expr initializer) {
            this.name = name;
            this.initializer = initializer;
        }

        @Override
        <R> R accept(Visitor<R> visitor) {
            return visitor.visitVarStmt(this);
        }
    }

    static class Block extends Stmt {

        List<Stmt> statements;

        public Block(List<Stmt> statements) {
            this.statements = statements;
        }

        @Override
        <R> R accept(Visitor<R> visitor) {
            return visitor.visitBlockStmt(this);
        }
    }

    static class Class extends Stmt {
        Token name;
        Expr.Variable superClass;
        List<Stmt.Function> methods;

        public Class(Token name, Expr.Variable superClass, List<Function> methods) {
            this.name = name;
            this.superClass = superClass;
            this.methods = methods;
        }

        @Override
        <R> R accept(Visitor<R> visitor) {
            return visitor.visitClassStmt(this);
        }
    }

    static class Function extends Stmt {

        Token name;
        List<Token> params;
        List<Stmt> body;

        public Function(Token name, List<Token> params, List<Stmt> body) {
            this.name = name;
            this.params = params;
            this.body = body;
        }

        @Override
        <R> R accept(Visitor<R> visitor) {
            return visitor.visitFunctionStmt(this);
        }
    }

    static class If extends Stmt {
        Expr condition;
        Stmt thenBranch;
        Stmt elseBranch;

        public If(Expr condition, Stmt thenBranch, Stmt elseBranch) {
            this.condition = condition;
            this.thenBranch = thenBranch;
            this.elseBranch = elseBranch;
        }

        @Override
        <R> R accept(Visitor<R> visitor) {
            return visitor.visitIfStmt(this);
        }
    }

    static class Return extends Stmt {
        Token keyword;
        Expr value;

        public Return(Token keyword, Expr value) {
            this.keyword = keyword;
            this.value = value;
        }

        @Override
        <R> R accept(Visitor<R> visitor) {
            return visitor.visitReturnStmt(this);
        }
    }

    static class While extends Stmt {
        Expr condition;
        Stmt body;

        public While(Expr condition, Stmt body) {
            this.condition = condition;
            this.body = body;
        }

        @Override
        <R> R accept(Visitor<R> visitor) {
            return visitor.visitWhileStmt(this);
        }
    }
}
